package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"math/rand"
	"net"
	"strings"
	"time"
)

type NodeInfo struct {
	NodeId     int    `json:"nodeId""`
	NodeIpAddr string `json:"nodeIpAddr"`
	Port       string `json:"port"`
}

func (node *NodeInfo) formatStringByNodeInfo() string {
	data, err := json.Marshal(&node)
	if err != nil {
		panic(err)
	}
	return "NodeInfo:" + string(data)
}

type AddToClusterMessage struct {
	Source  NodeInfo `json:"source"`
	Dest    NodeInfo `json:"dest"`
	Message string   `json:"message"`
}

func (req *AddToClusterMessage) formatStringByAddToClusterMessage() string {

	data, err := json.Marshal(&req)
	if err != nil {
		panic(err)
	}
	return "AddToClusterMessage:" + string(data)
}

//获取追加到服务的信息
func getAddToClusterMessage(source NodeInfo, dest NodeInfo, message string) AddToClusterMessage {
	return AddToClusterMessage{
		Source: NodeInfo{
			NodeId:     source.NodeId,
			NodeIpAddr: source.NodeIpAddr,
			Port:       source.Port,
		},
		Dest: NodeInfo{
			NodeId:     dest.NodeId,
			NodeIpAddr: dest.NodeIpAddr,
			Port:       dest.Port,
		},
		Message: message,
	}
}

//链接追加到服务器的信息
func connectToCluster(me NodeInfo, dest NodeInfo) bool {
	//链接sorket
	connOut, err := net.DialTimeout("tcp", dest.NodeIpAddr+":"+dest.Port, time.Duration(10)*time.Second)

	if err != nil {
		if _, ok := err.(net.Error); ok {
			fmt.Println("没有连接到集群:", me.NodeId)
			return false
		}

	} else {
		fmt.Println("链接到集群，发送消息到节点")
		text := "hi,老大，请添加我到节点"
		requestMessage := getAddToClusterMessage(me, dest, text) //请求消息
		json.NewEncoder(connOut).Encode(&requestMessage)         //编码
		decoder := json.NewDecoder(connOut)                      //解码
		var responseMessage AddToClusterMessage
		decoder.Decode(&responseMessage)
		fmt.Println("得到数据相应: \n " + responseMessage.formatStringByAddToClusterMessage())
		return true
	}

	return false
}

//接听
func listOnPost(me NodeInfo) {
	//监听信息
	ln, _ := net.Listen("tcp", fmt.Sprint(":"+me.Port))
	for {

		connIn, err := ln.Accept() //监听
		if err != nil {
			if _, ok := err.(net.Error); ok {
				fmt.Sprint("收信息的时候出现网络错误", me.NodeId)
			}
		} else {

			var requestMessage AddToClusterMessage          //处理要加入的节点
			json.NewDecoder(connIn).Decode(&requestMessage) //解码
			fmt.Println("获得请求：\n" + requestMessage.formatStringByAddToClusterMessage())
			text := "确定啊，小伙子，欢迎加入"
			responseMessage := getAddToClusterMessage(me, requestMessage.Source, text)
			json.NewEncoder(connIn).Encode(&responseMessage) //编码
			connIn.Close()
		}

	}

}

func main() {
	//node := NodeInfo{
	//	NodeId:     25,
	//	NodeIpAddr: "127.0.0.1",
	//	Port:       "2589",
	//}
	//
	//nodeStr :=node.formatStringByNodeInfo()
	//fmt.Println(nodeStr)

	fmt.Println("game start !")
	makeMasterOnError := flag.Bool("makeMasterOnError", false, "没有链接集群，我们将本ip设置Master节点")
	clusterIp := flag.String("clusterIp", "127.0.0.1:8001", "任何节点可以链接这个ip")
	myPort := flag.String("myPort", "8001", "ip addr 正在运行这个节点，端口号是8001")
	flag.Parse() //解析命令行

	fmt.Println("makeMasterOnError:", *makeMasterOnError)
	fmt.Println("clusterIp:", *clusterIp)
	fmt.Println("myPort:", *myPort)

	//节点生成ID
	rand.Seed(time.Now().UTC().UnixNano()) //随机数种子
	myId := rand.Intn(999999999)           //生成随机数
	fmt.Println("节点生成ID:", myId)

	//抓取ip地址
	myIp, _ := net.InterfaceAddrs()
	fmt.Println("抓取ip地址:", myIp[0])

	//创建当前节点信息
	me := NodeInfo{
		NodeId:     myId,
		NodeIpAddr: myIp[0].String(),
		Port:       *myPort,
	}
	fmt.Println("当前节点：", me)
	dest := NodeInfo{
		NodeId:     -1,
		NodeIpAddr: strings.Split(*clusterIp, ":")[0],
		Port:       strings.Split(*clusterIp, ":")[1],
	}

	fmt.Println("目标节点：", dest)

	//尝试链接
	ableToConnect := connectToCluster(me, dest)
	//监听其他节点加入集群
	if ableToConnect || (!ableToConnect && *makeMasterOnError) {
		if *makeMasterOnError {
			fmt.Println("当前节点作为主节点！")
		}
		listOnPost(me)
	} else {
		fmt.Println("退出系统")
	}
}
